package cn.jiedanba.cacert.ca.service.impl;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;

import cn.jiedanba.cacert.ca.service.CertService;
import cn.jiedanba.cacert.ca.vo.GetCertVo;
import cn.jiedanba.cacert.ca.vo.QueryVo;
import cn.jiedanba.cacert.ca.vo.RevokeVo;
import cn.jiedanba.cacert.common.ca.PkiUtil;
import cn.jiedanba.cacert.common.ca.algorithm.SignatureAlgorithms;
import cn.jiedanba.cacert.common.ca.cert.PkiCertUtil;
import cn.jiedanba.cacert.common.ca.cert.enums.CertStatusEnum;
import cn.jiedanba.cacert.common.ca.cert.enums.CertTypeEnum;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCert;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCertCustomExtension;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCertPolicy;
import cn.jiedanba.cacert.common.ca.generator.RandomSerialNumberGenerator;
import cn.jiedanba.cacert.common.config.Configs;
import cn.jiedanba.cacert.common.result.ResponseResult;
import cn.jiedanba.cacert.common.util.DateUtil;
import cn.jiedanba.cacert.mapper.CertCustomExtensionMapper;
import cn.jiedanba.cacert.mapper.CertFileMapper;
import cn.jiedanba.cacert.mapper.CertMapper;
import cn.jiedanba.cacert.mapper.CertPolicyMapper;
import cn.jiedanba.cacert.mapper.CertRevokeMapper;
import cn.jiedanba.cacert.mapper.CertTemplateMapper;
import cn.jiedanba.cacert.mapper.KeyUsageMapper;
import cn.jiedanba.cacert.mapper.RootCaMapper;
import cn.jiedanba.cacert.model.Cert;
import cn.jiedanba.cacert.model.CertCustomExtension;
import cn.jiedanba.cacert.model.CertFile;
import cn.jiedanba.cacert.model.CertPolicy;
import cn.jiedanba.cacert.model.CertRevoke;
import cn.jiedanba.cacert.model.CertTemplate;
import cn.jiedanba.cacert.model.KeyUsage;
import cn.jiedanba.cacert.model.RootCa;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class CertServiceImpl implements CertService {

	@Resource
	private CertMapper certMapper;
	@Autowired
	private CertFileMapper certFileMapper;
	@Resource
	private CertTemplateMapper certTemplateMapper;
	@Resource
	private RootCaMapper rootCaMapper;
	@Resource
	private KeyUsageMapper keyUsageMapper;
	@Resource
	private CertPolicyMapper certPolicyMapper;
	@Resource
	private CertCustomExtensionMapper certCustomExtensionMapper;
	@Resource
	private CertRevokeMapper certRevokeMapper;

	/**
	 * 获取证书
	 * 
	 * @param vo
	 * @return
	 */
	@Transactional
	@Override
	public ResponseResult getCert(GetCertVo vo) {
		try {

			CertTemplate template = certTemplateMapper.selectOneById(vo.getTemplateId());
			if (template == null) {
				return ResponseResult.fail("证书模板不存在");
			}

			RootCa rootCa = rootCaMapper.selectOneById(template.getRootId());
			if (rootCa == null) {
				return ResponseResult.fail("根证书不存在");
			}
			Date now = DateUtil.getNTPDate(Configs.get("ntp.url"));
			Date endTime = DateUtil.getNextDay(now, Integer.parseInt(vo.getDay()));

			if (endTime.getTime() >= rootCa.getNotAfter().getTime()) {
				return ResponseResult.fail("证书天数不能大于根证书过期时间");
			}

			// 颁发者私钥和证书
			PrivateKey issuerPrivateKey = PkiUtil.getPrivateKey(Base64.decodeBase64(rootCa.getPrivateKey()));
			X509Certificate issuerCert = PkiUtil.readX509Certificate(Base64.decodeBase64(rootCa.getSignCert()));

			PkiCert pkiCert = new PkiCert();
			pkiCert.setCA(false);
			String[] split = vo.getSubjectDn().split(",");
			String email = null;
			String cn = null;
			String ou = null;
			String o = null;
			String l = null;
			String st = null;
			String c = null;
			for (String x : split) {
				if (x.contains("E=") || x.contains("e=")) {
					email = x.trim().replaceAll("E=", "").replaceAll("e=", "");
				} else if (x.contains("CN=") || x.contains("cn=")) {
					cn = x.trim().replaceAll("CN=", "").replaceAll("cn=", "");
				} else if (x.contains("OU=") || x.contains("ou=")) {
					ou = x.trim().replaceAll("OU=", "").replaceAll("ou=", "");
				} else if (x.contains("O=") || x.contains("o=")) {
					o = x.trim().replaceAll("O=", "").replaceAll("o=", "");
				} else if (x.contains("L=") || x.contains("l=")) {
					l = x.trim().replaceAll("L=", "").replaceAll("l=", "");
				} else if (x.contains("ST=") || x.contains("st=")) {
					st = x.trim().replaceAll("ST=", "").replaceAll("st=", "");
				} else if (x.contains("S=") || x.contains("s=")) {
					st = x.trim().replaceAll("S=", "").replaceAll("s=", "");
				} else if (x.contains("C=") || x.contains("c=")) {
					c = x.trim().replaceAll("C=", "").replaceAll("c=", "");

				}
			}

			X500Name subjectName = PkiCertUtil.buildName(email, cn, ou, o, l, st, c);

			// 证书主题
			pkiCert.setSubjectDN(subjectName);
			// 公钥
			pkiCert.setSubjectPublicKey(PkiUtil.getPublicKey(Base64.decodeBase64(vo.getPubKey())));
			// 密钥基本用法
			String[] usages = template.getKeyUsage().split(",");
			List<Integer> usagesOid = new ArrayList<>();
			for (String string : usages) {
				KeyUsage keyUsage = keyUsageMapper.selectOneById(string);
				usagesOid.add(Integer.parseInt(keyUsage.getUsageValue()));
			}
			pkiCert.setUsage(usagesOid.toArray(new Integer[usagesOid.size()]));

			// 密钥增强用法
			if (template.getExtendUsage() != null) {
				String[] extendUsages = template.getExtendUsage().split(",");
				List<String> extendUsageOids = new ArrayList<String>();
				for (String string : extendUsages) {
					KeyUsage keyUsage = keyUsageMapper.selectOneById(string);
					extendUsageOids.add(keyUsage.getUsageValue());
				}
				pkiCert.setExtendUsage(extendUsageOids.toArray(new String[extendUsageOids.size()]));
			}
			// 序列号
			pkiCert.setSerialNumber(RandomSerialNumberGenerator.getInstance().nextSerialNumber(15));
			// 生效时间
			pkiCert.setStartTime(now);
			// 截止时间
			pkiCert.setEndTime(endTime);
			// 颁发者证书
			pkiCert.setIssuerCert(issuerCert);
			// 颁发者私钥
			pkiCert.setIssuerPrivateKey(issuerPrivateKey);
			// 证书签名算法
			pkiCert.setSignatureAlgorithm(template.getSignatureAlgorithm());
			// crl分发节点
			pkiCert.setCrl(template.getCrl());
			// 颁发者crt
			pkiCert.setCrt(template.getCrt());
			// ocsp在线状态查询
			pkiCert.setOscp(template.getOcsp());

			// 证书颁发策略
			List<PkiCertPolicy> policyVos = new ArrayList<>();
			List<CertPolicy> policies = certPolicyMapper.findByTemplateId(template.getId());
			for (CertPolicy certificatePolicy : policies) {
				PkiCertPolicy policyVo = new PkiCertPolicy();
				policyVo.setPolicyOid(certificatePolicy.getPolicyOid());
				policyVo.setPolicyValue(certificatePolicy.getPolicyValue());
				policyVos.add(policyVo);
			}
			pkiCert.setPolicyVos(policyVos);

			// 自定义证书扩展项
			List<PkiCertCustomExtension> extensionVos = new ArrayList<>();
			List<CertCustomExtension> extensions = certCustomExtensionMapper.findByTemplateId(template.getId());
			for (CertCustomExtension certificateCustomExtension : extensions) {
				PkiCertCustomExtension extensionVo = new PkiCertCustomExtension();
				extensionVo.setOid(certificateCustomExtension.getOid());
				extensionVo.setValue(certificateCustomExtension.getValue());
				extensionVos.add(extensionVo);
			}
			pkiCert.setExtensions(extensionVos);

			// 制证
			X509Certificate signCert = PkiCertUtil.buildSubjectCert(pkiCert);

			Cert cert = new Cert();
			cert.setRootId(template.getRootId());
			cert.setTemplateId(template.getId());
			RDN CN = pkiCert.getSubjectDN().getRDNs(BCStyle.CN)[0];
			cert.setCertName(CN.getFirst().getValue().toString());
			cert.setCertType(CertTypeEnum.SIGN.code);
			cert.setSerialNumber(PkiUtil.serialNumberConvertString(signCert.getSerialNumber()).toUpperCase());
			cert.setSubjectDn(PkiUtil.subjectDnSort(signCert.getSubjectDN().toString()));
			cert.setSigAlgName(SignatureAlgorithms.getSignatureName(signCert.getSigAlgOID()));
			cert.setSigAlgOid(signCert.getSigAlgOID());
			cert.setPublicKeySize(PkiUtil.getKeyLength(signCert.getPublicKey()));
			cert.setNotBefore(signCert.getNotBefore());
			cert.setNotAfter(signCert.getNotAfter());
			cert.setStatus(CertStatusEnum.NORMAL.code);
			cert.setCreateDate(now);
			certMapper.insertSelective(cert);

			CertFile certFile = new CertFile();
			certFile.setCertId(cert.getId());
			certFile.setSignCert(Base64.encodeBase64String(signCert.getEncoded()));
			certFile.setCreateDate(now);
			certFileMapper.insertSelective(certFile);

			ModelMap result = new ModelMap();
			result.put("issuerDn", rootCa.getSubjectDn());
			result.put("sn", cert.getSerialNumber());
			result.put("subjectDn", cert.getSubjectDn());
			result.put("sigAlgName", cert.getSigAlgName());
			result.put("sigAlgOid", cert.getSigAlgOid());
			result.put("notBefore", cert.getNotBefore());
			result.put("notAfter", cert.getNotAfter());
			result.put("status", cert.getStatus());
			result.put("keyUsage", template.getKeyUsage());
			result.put("extendUsage", template.getExtendUsage());
			result.put("signCert", certFile.getSignCert());

			return ResponseResult.success(result);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return ResponseResult.error("制证失败");
		}

	}

	/**
	 * 证书吊销
	 * 
	 * @param vo
	 * @return
	 */
	@Transactional
	@Override
	public ResponseResult revoke(RevokeVo vo) {
		try {
			Cert cert = certMapper.findCertBySn(vo.getSn());
			if (cert == null) {
				return ResponseResult.fail("证书不存在");
			}

			if (StringUtils.equalsIgnoreCase(cert.getStatus(), CertStatusEnum.REVOKE.code)) {
				return ResponseResult.fail("证书已被吊销");
			}
			if (StringUtils.equalsIgnoreCase(cert.getStatus(), CertStatusEnum.EXPIRE.code)) {
				return ResponseResult.fail("证书已过期");
			}

			if (StringUtils.isBlank(vo.getReason())) {
				vo.setReason("0");
			}
			Date now = DateUtil.getNTPDate(Configs.get("ntp.url"));
			CertRevoke revoke = new CertRevoke();
			revoke.setCertId(cert.getId());
			revoke.setRootId(cert.getRootId());
			revoke.setReason(Integer.valueOf(vo.getReason()));
			revoke.setIsCrl(false);
			revoke.setSerialNumber(cert.getSerialNumber());
			revoke.setRevokeDate(now);

			revoke.setCreateDate(now);
			certRevokeMapper.insertSelective(revoke);

			cert.setRevokeDate(now);
			cert.setStatus(CertStatusEnum.REVOKE.code);
			cert.setUpdateDate(now);
			certMapper.update(cert);
			return ResponseResult.success("吊销成功");
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return ResponseResult.error("吊销失败");
		}

	}

	/**
	 * 查询证书
	 * 
	 * @param vo
	 * @return
	 */
	@Override
	public ResponseResult query(QueryVo vo) {
		try {
			Cert cert = certMapper.findCertBySn(vo.getSn());
			if (cert == null) {
				return ResponseResult.fail("证书不存在");
			}

			CertFile file = certFileMapper.findByCertId(cert.getId());
			if (file == null) {
				return ResponseResult.fail("证书不存在");
			}

			RootCa rootCa = rootCaMapper.selectOneById(cert.getRootId());

			ModelMap result = new ModelMap();
			result.put("issuerDn", rootCa.getSubjectDn());
			result.put("sn", cert.getSerialNumber());
			result.put("subjectDn", cert.getSubjectDn());
			result.put("sigAlgName", cert.getSigAlgName());
			result.put("sigAlgOid", cert.getSigAlgOid());
			result.put("notBefore", cert.getNotBefore());
			result.put("notAfter", cert.getNotAfter());
			result.put("status", cert.getStatus());
			result.put("signCert", file.getSignCert());

			return ResponseResult.success(result);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return ResponseResult.error("查询失败");
		}
	}

}
